// Copyright 2010, Shuo Chen.  All rights reserved.
// ... (license and author) ...

#include "src/net/poller/EPollPoller.h" // Use your new EPollPoller.h path

#include "src/base/Logging.h"  // Replaced by your logging
#include "src/net/Channel.h"   // Use your Channel.h path
#include "src/net/EventLoop.h" // For assertInLoopThread via Poller base

#include <cassert> // For assert
#include <cerrno>  // For errno
#include <cstring> // For memZero (if you define it, or use std::memset)
#include <poll.h>  // For POLLIN etc. constants (still useful for static_assert)
#include <sys/epoll.h>
#include <unistd.h> // For ::close

// // Define memZero if not available elsewhere
// #ifndef memZero
// inline void memZero(void* p, size_t n)
// {
//   std::memset(p, 0, n);
// }
// #endif

namespace sola // Or your chosen namespace
{
namespace net {

// On Linux, the constants of poll(2) and epoll(4)
// are expected to be the same.
static_assert(EPOLLIN == POLLIN, "epoll uses same flag values as poll");
static_assert(EPOLLPRI == POLLPRI, "epoll uses same flag values as poll");
static_assert(EPOLLOUT == POLLOUT, "epoll uses same flag values as poll");
static_assert(EPOLLRDHUP == POLLRDHUP, "epoll uses same flag values as poll");
static_assert(EPOLLERR == POLLERR, "epoll uses same flag values as poll");
static_assert(EPOLLHUP == POLLHUP, "epoll uses same flag values as poll");

namespace {
const int kNew     = -1; // Channel not in poller
const int kAdded   = 1;  // Channel added to poller
const int kDeleted = 2;  // Channel was in poller, now marked for deletion (or just removed)
} // namespace

EPollPoller::EPollPoller(EventLoop* loop)
    : Poller(loop)
    , // Call base class constructor
    epollfd_(::epoll_create1(EPOLL_CLOEXEC))
    , events_(kInitEventListSize) {
    if (epollfd_ < 0) {
        // LOG_SYSFATAL << "EPollPoller::EPollPoller" << std::endl; // Use your logging
        LOG_SYSFATAL;
    }
}

EPollPoller::~EPollPoller() {
    ::close(epollfd_);
}

sola::Timestamp EPollPoller::poll(int timeoutMs, ChannelList* activeChannels) {
    // LOG_TRACE << "fd total count " << channels_.size(); // Use your logging
    int             numEvents  = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs);
    int             savedErrno = errno;
    sola::Timestamp now(sola::Timestamp::now());
    if (numEvents > 0) {
        // LOG_TRACE << numEvents << " events happened"; // Use your logging
        fillActiveChannels(numEvents, activeChannels);
        if (static_cast<size_t>(numEvents) == events_.size()) {
            events_.resize(events_.size() * 2);
        }
    } else if (numEvents == 0) {
        // LOG_TRACE << "nothing happened"; // Use your logging
    } else {
        if (savedErrno != EINTR) {
            errno = savedErrno;
            LOG_SYSERR << "EPollPoller::poll()" << std::endl; // Use your logging
        }
    }
    return now;
}

// fillActiveChannels now non-const because it might access channels_ for debug checks
void EPollPoller::fillActiveChannels(int numEvents, ChannelList* activeChannels) {
    assert(static_cast<size_t>(numEvents) <= events_.size());
    for (int i = 0; i < numEvents; ++i) {
        Channel* channel = static_cast<Channel*>(events_[i].data.ptr);
#ifndef NDEBUG
        int                        fd = channel->fd();
        ChannelMap::const_iterator it = channels_.find(fd);
        assert(it != channels_.end());
        assert(it->second == channel);
#endif
        channel->set_revents(events_[i].events);
        activeChannels->push_back(channel);
    }
}

void EPollPoller::updateChannel(Channel* channel) {
    Poller::assertInLoopThread(); // Call base class method
    const int index = channel->index();
    // LOG_TRACE << "fd = " << channel->fd()
    //   << " events = " << channel->events() << " index = " << index;

    if (index == kNew || index == kDeleted) {
        int fd = channel->fd();
        if (index == kNew) {
            assert(channels_.find(fd) == channels_.end());
            channels_[fd] = channel;
        } else // index == kDeleted
        {
            assert(channels_.find(fd) != channels_.end());
            assert(channels_[fd] == channel);
            // If it was kDeleted, it means it was previously in epoll but removed.
            // Now we are re-adding it.
        }
        channel->set_index(kAdded);
        update(EPOLL_CTL_ADD, channel);
    } else // index == kAdded
    {
        int fd = channel->fd();
        (void)fd; // Suppress unused variable warning in release
        assert(channels_.find(fd) != channels_.end());
        assert(channels_[fd] == channel);
        assert(index == kAdded);
        if (channel->isNoneEvent()) {
            update(EPOLL_CTL_DEL, channel);
            channel->set_index(kDeleted); // Mark as deleted from epoll, but still known to Poller (in channels_ map)
                                          // until removeChannel
        } else {
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

void EPollPoller::removeChannel(Channel* channel) {
    Poller::assertInLoopThread(); // Call base class method
    int fd = channel->fd();
    // LOG_TRACE << "fd = " << fd;
    assert(channels_.find(fd) != channels_.end());
    assert(channels_[fd] == channel);
    assert(channel->isNoneEvent()); // Typically, disableAll events before removing

    int index = channel->index();
    assert(index == kAdded || index == kDeleted); // Must be kAdded if still in epoll, or kDeleted if already
                                                  // epoll_ctl_del'd by updateChannel

    size_t n = channels_.erase(fd);
    assert(n == 1);
    (void)n; // Suppress unused variable warning

    if (index == kAdded) // If it was kAdded, it means it's still in epoll's interest list
    {
        update(EPOLL_CTL_DEL, channel);
    }
    channel->set_index(kNew); // Mark as completely new/removed
}

bool EPollPoller::hasChannel(Channel* channel) const {
    Poller::assertInLoopThread(); // Call base class method
    ChannelMap::const_iterator it = channels_.find(channel->fd());
    return it != channels_.end() && it->second == channel;
}

void EPollPoller::update(int operation, Channel* channel) {
    struct epoll_event event;
    //   memZero(&event, sizeof event); // Use your memZero or std::memset
    std::memset(&event, 0, sizeof event); // Use std::memset for safety
    event.events   = channel->events();
    event.data.ptr = channel; // Store Channel pointer
    int fd         = channel->fd();

    // LOG_TRACE << "epoll_ctl op = " << operationToString(operation)
    //   << " fd = " << fd << " event = { " << channel->eventsToString() << " }";

    if (::epoll_ctl(epollfd_, operation, fd, &event) < 0) {
        if (operation == EPOLL_CTL_DEL) {
            LOG_SYSERR << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd << std::endl;
        } else {
            //   LOG_SYSFATAL << "epoll_ctl op =" << operationToString(operation) << " fd =" << fd << std::endl;
            LOG_SYSFATAL;
        }
    }
}

const char* EPollPoller::operationToString(int op) // Static method
{
    switch (op) {
    case EPOLL_CTL_ADD:
        return "ADD";
    case EPOLL_CTL_DEL:
        return "DEL";
    case EPOLL_CTL_MOD:
        return "MOD";
    default:
        assert(false && "ERROR op");
        return "Unknown Operation";
    }
}

} // namespace net
} // namespace sola
